package cn.storm.codehelper

import cn.storm.codehelper.model.ObjConfig
import cn.storm.codehelper.util.*
import com.intellij.configurationStore.NOTIFICATION_GROUP_ID
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache


val exclude = listOf<String>(
    "java.lang.String",
    "java.lang.Boolean",
    "java.lang.Integer",
    "java.lang.Long",
    "java.time.LocalDateTime",
    "java.time.LocalTime",
    "java.time.LocalDate",
)


class BeanTransAction:AnAction() {
    override fun actionPerformed(e: AnActionEvent) {
        val project = e.project
        if (project.isNull()) {
            return
        }
        val file = e.getData(CommonDataKeys.PSI_FILE)
        if (file.isNull()) {
            return
        }
        val javaFile = file as PsiJavaFile
        //目标bean
        val targetClass = javaFile.classes.firstOrNull()
        if (targetClass.isNull()) {
            return
        }
        val setConfig = collSets(targetClass!!)
        //获取粘贴板中的内容 一般为一个或者过个来源bean
        val getObjConfigs = formatStr2ClazzNameWords(getSystemClipboardText())
            .map { collGets(it, project!!, javaFile) }
        if (getObjConfigs.isEmpty()
            ||(getObjConfigs.size==1 && getObjConfigs[0].paramMtdMap.isEmpty())) {
            notify(Notification(NOTIFICATION_GROUP_ID, "请复制方法入参", NotificationType.ERROR))
            return
        }
        //拼接插入内容
        val result = """if (ObjectUtil.hasNull(${getObjConfigs.filter { it.clazzParam.isNotNull()&&it.clazzParam.isNotBlank()&&it.paramMtdMap.isNotEmpty() }.joinToString(",") { it.clazzParam }})) {
            //todo 非空处理
            return null;
        }
        ${setConfig.clazzName} ${setConfig.clazzParam} = new ${setConfig.clazzName}();
        ${
            setConfig.params.joinToString("") {
                "${setConfig.clazzParam}.${setConfig.paramMtdMap[it]}(${
                    getObjConfigs.firstOrNull { get ->
                        //map中包含此参数 获取map为空，本身等于此参数
                        get.paramMtdMap.containsKey(it) || (get.paramMtdMap.isEmpty() && get.clazzParam == it)
                    }.let { get ->
                        if (get?.paramMtdMap?.containsKey(it) == true) {
                            get.paramMtdMap[it].run { "${get.clazzParam}.$this()" }
                        }else if (get?.clazzParam == it) {
                            get.clazzParam
                        } else {
                            ""
                        }
                    }
                });\n        "
            }
        }
        return ${setConfig.clazzParam};"""
        //写入内容
        val craet = e.getData(LangDataKeys.CARET)
        craet?.apply {
            ApplicationManager.getApplication().runWriteAction{
                WriteCommandAction.runWriteCommandAction(e.project){
                    WriteAction.run<RuntimeException> {
                        EditorModificationUtil.insertStringAtCaret(editor, result)
                    }
                }
            }
        }
    }

    private fun collSets(psiClass: PsiClass): ObjConfig {
        val paramMtdMap = HashMap<String, String>()
        val params = mutableListOf<String>()
        getClassLinkList(psiClass)
            .flatMap {
                getMethods(it, "set")
            }.distinct()
            .forEach {
                if (it.startsWith("is")) {
                    val param = it.replaceFirst("is", "").replaceFirstChar { f -> f.lowercase() }
                    paramMtdMap[param] = it
                    params.add(param)
                } else {
                    val param = it.replaceFirst("set", "").replaceFirstChar { f -> f.lowercase() }
                    paramMtdMap[param] = it
                    params.add(param)
                }
            }
        return ObjConfig(
            psiClass.qualifiedName ?: "",
            psiClass.name!!,
            psiClass.name!!.replaceFirstChar { it.lowercase() },
            params,
            paramMtdMap
        )
    }



    /**
     * 将入参进行拆分
     */
    private fun formatStr2ClazzNameWords(str: String): List<String> {
        return str.replace("\n", "")
            .split(",")
            .map { it.trim() }
            .map { it.replace(Regex("\\s_"), " ") }
    }

    private fun collGets(clazzNameWord: String, project: Project, javaFile: PsiJavaFile): ObjConfig {
        val list = clazzNameWord.split(Regex("\\s"))
        if (list.size < 2) {
            return ObjConfig("", "", "", listOf(), HashMap())
        }
        val clazzNameStr = list[0]
        val clazzParam = list[1]
        val clazzName =
            if (clazzNameStr.indexOf(".") > 0) clazzNameStr.substringAfterLast(".") else clazzNameStr
        val psiClazzs =
            PsiShortNamesCache.getInstance(project).getClassesByName(clazzName, GlobalSearchScope.allScope(project))
        val psiClass =
            if (psiClazzs.size == 1) psiClazzs[0]
            else if (psiClazzs.isEmpty()) null
            else {
                val imports = javaFile.importList
                val importStrs = imports?.allImportStatements?.map { it.text }
                psiClazzs.firstOrNull {
                    var result = false
                    val qualifiedName = it.qualifiedName
                    result = importStrs?.any {
                            s -> s.contains(qualifiedName?:"")
                    } ?: false
                    if (!result) {
                        result = qualifiedName?.contains(javaFile.packageName) ?: false
                    }
                    result
                } ?: psiClazzs[0]
            }

        if (psiClass.isNull()) {
            return ObjConfig("", clazzNameStr, clazzParam, listOf(), HashMap())
        }
        val paramMtdMap = HashMap<String, String>()

        if (exclude.contains(psiClass!!.qualifiedName?:"")){
            return ObjConfig(psiClass.qualifiedName ?: "", clazzNameStr, clazzParam, listOf(), paramMtdMap)
        }

        getClassLinkList(psiClass!!)
            .flatMap {
                getMethods(it, "get")
            }.distinct()
            .forEach {
                paramMtdMap[it.replaceFirst("get", "").replaceFirstChar { f -> f.lowercase() }] = it
            }
        return ObjConfig(psiClass.qualifiedName ?: "", clazzNameStr, clazzParam, listOf(), paramMtdMap)
    }
}

